package com.zzg.mybatis.generator.bridge;

import com.zzg.mybatis.generator.model.DatabaseConfig;
import com.zzg.mybatis.generator.model.DbType;
import com.zzg.mybatis.generator.model.GeneratorConfig;
import com.zzg.mybatis.generator.model.TableFxModel;
import com.zzg.mybatis.generator.plugins.CommentExtensionGenerator;
import com.zzg.mybatis.generator.plugins.VoCommentExtensionGenerator;
import com.zzg.mybatis.generator.util.ConfigHelper;
import com.zzg.mybatis.generator.util.DbUtil;
import com.zzg.mybatis.generator.view.AlertUtil;
import org.apache.commons.lang3.StringUtils;
import org.mybatis.generator.api.MyBatisGenerator;
import org.mybatis.generator.api.ProgressCallback;
import org.mybatis.generator.api.ShellCallback;
import org.mybatis.generator.config.*;
import org.mybatis.generator.internal.DefaultShellCallback;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;
import java.util.stream.Collectors;

public class MybatisGeneratorBridge {
    private static final String VO_GENERATE = "_vo_generate_";
    private static final String OTHER_GENERATE = "_other_generate_";
    private static final Logger _LOG = LoggerFactory.getLogger(MybatisGeneratorBridge.class);

    private ProgressCallback progressCallback;

    private List<ColumnOverride> columnOverrides;

    public MybatisGeneratorBridge() {
    }

    /**
     * 根据配置以及表选项生成表相关的Vo以及entity
     * @param generatorConfig  配置
     * @param tableFxModels    表集合
     * @param ignoredColumns
     * @param currentOperation
     */
    public void generateVo(GeneratorConfig generatorConfig, List<TableFxModel> tableFxModels, Map<String, List<IgnoredColumn>> ignoredColumns, String currentOperation) throws Exception {
        if (tableFxModels == null || tableFxModels.stream().map(x -> x.getDatabaseConfig() != null).count() <= 0) {
            AlertUtil.showErrorAlert("选择表的数据库连接找不到");
            return;
        }
        if (tableFxModels.stream().filter(x -> x.getDatabaseConfig() != null && StringUtils.isNotBlank(x.getDatabaseConfig().getName())).map(x -> x.getDatabaseConfig().getName()).distinct().count() > 1) {
            AlertUtil.showErrorAlert("单次生成只能选择同一个库的表进行生成");
            return;
        }
        DatabaseConfig selectedDatabaseConfig = tableFxModels.stream().filter(x -> x.getDatabaseConfig() != null).map(x -> x.getDatabaseConfig()).collect(Collectors.toList()).get(0);
        Configuration configuration = new Configuration();
        Context context = new Context(ModelType.CONDITIONAL);
        configuration.addContext(context);
        context.addProperty("javaFileEncoding", "UTF-8");
        String connectorLibPath = ConfigHelper.findConnectorLibPath(selectedDatabaseConfig.getDbType());
        configuration.addClasspathEntry(connectorLibPath);

        JDBCConnectionConfiguration jdbcConfig = new JDBCConnectionConfiguration();
        jdbcConfig.setDriverClass(DbType.valueOf(selectedDatabaseConfig.getDbType()).getDriverClass());
        jdbcConfig.setConnectionURL(DbUtil.getConnectionUrlWithSchema(selectedDatabaseConfig));
        jdbcConfig.setUserId(selectedDatabaseConfig.getUsername());
        jdbcConfig.setPassword(selectedDatabaseConfig.getPassword());
        jdbcConfig.addProperty("remarksReporting", "true");

        // java model
        JavaModelGeneratorConfiguration modelConfig = new JavaModelGeneratorConfiguration();
        modelConfig.setTargetPackage(generatorConfig.getModelPackage());
        modelConfig.setTargetProject(generatorConfig.getProjectFolder() + "/" + generatorConfig.getModelPackageTargetFolder());
        // Mapper configuration
        SqlMapGeneratorConfiguration mapperConfig = new SqlMapGeneratorConfiguration();
        mapperConfig.setTargetPackage(generatorConfig.getMappingXMLPackage());
        mapperConfig.setTargetProject(generatorConfig.getProjectFolder() + "/" + generatorConfig.getMappingXMLTargetFolder());
        // DAO
        JavaClientGeneratorConfiguration daoConfig = new JavaClientGeneratorConfiguration();
        daoConfig.setConfigurationType("XMLMAPPER");
        daoConfig.setTargetPackage(generatorConfig.getDaoPackage());
        daoConfig.setTargetProject(generatorConfig.getProjectFolder() + "/" + generatorConfig.getDaoTargetFolder());

        context.setId("myid");
        // 表配置项
        for (TableFxModel tableFxModel : tableFxModels) {
            TableConfiguration tableConfig = new TableConfiguration(context);
            tableConfig.setTableName(tableFxModel.getTableName());
            tableConfig.setDomainObjectName(tableFxModel.getDomainName());
            // 针对 postgresql 单独配置
            if (DbType.valueOf(selectedDatabaseConfig.getDbType()).getDriverClass() == "org.postgresql.Driver") {
                tableConfig.setDelimitIdentifiers(true);
            }
            //添加GeneratedKey主键生成
            if (StringUtils.isNoneEmpty(generatorConfig.getGenerateKeys())) {
                tableConfig.setGeneratedKey(new GeneratedKey(generatorConfig.getGenerateKeys(), selectedDatabaseConfig.getDbType(), true, null));
            }
            if (generatorConfig.getMapperName() != null) {
                tableConfig.setMapperName(generatorConfig.getMapperName());
            }
            //TODO:add ignore columns
            if (ignoredColumns != null && ignoredColumns.containsKey(tableFxModel.getTableName())) {
                ignoredColumns.get(tableFxModel.getTableName()).stream().forEach(ignoredColumn -> {
                    tableConfig.addIgnoredColumn(ignoredColumn);
                });
            }
            if (columnOverrides != null) {
                columnOverrides.stream().forEach(columnOverride -> {
                    tableConfig.addColumnOverride(columnOverride);
                });
            }
            if (generatorConfig.isUseActualColumnNames()) {
                tableConfig.addProperty("useActualColumnNames", "true");
            }
            context.addTableConfiguration(tableConfig);
        }
        JavaTypeResolverConfiguration javaTypeResolverConfiguration = new JavaTypeResolverConfiguration();
        javaTypeResolverConfiguration.addProperty("forceBigDecimals", "true");
        context.setJavaTypeResolverConfiguration(javaTypeResolverConfiguration);
        context.setJdbcConnectionConfiguration(jdbcConfig);
        context.setJavaModelGeneratorConfiguration(modelConfig);
        // Comment
        PluginConfiguration pluginConfigurationForLombok = new PluginConfiguration();
        pluginConfigurationForLombok.setConfigurationType("com.zzg.mybatis.generator.plugins.LombokPlugin");
        pluginConfigurationForLombok.addProperty("type", "com.zzg.mybatis.generator.plugins.LombokPlugin");
        pluginConfigurationForLombok.addProperty("hasLombok", "false");
        CommentGeneratorConfiguration commentConfig = new CommentGeneratorConfiguration();

        switch (currentOperation) {
            case VO_GENERATE:
                context.addPluginConfiguration(pluginConfigurationForLombok);
                commentConfig.setConfigurationType(VoCommentExtensionGenerator.class.getName());
                break;
            case OTHER_GENERATE:
                commentConfig.setConfigurationType(CommentExtensionGenerator.class.getName());
                if (generatorConfig.isXmlWithMapperCheckBoxChecked()) {
                    context.setJavaClientGeneratorConfiguration(daoConfig);
                    context.setSqlMapGeneratorConfiguration(mapperConfig);
                }
                break;
        }
        if (generatorConfig.isComment()) {

        }
        commentConfig.addProperty("columnRemarks", "true");
        commentConfig.addProperty("remarksReporting", "true");
        if (generatorConfig.isAnnotation()) {
            commentConfig.addProperty("annotations", "true");
        }
        context.setCommentGeneratorConfiguration(commentConfig);

        //实体添加序列化
        PluginConfiguration serializablePluginConfiguration = new PluginConfiguration();
        serializablePluginConfiguration.addProperty("type", "org.mybatis.generator.plugins.SerializablePlugin");
        serializablePluginConfiguration.setConfigurationType("org.mybatis.generator.plugins.SerializablePlugin");
        context.addPluginConfiguration(serializablePluginConfiguration);
        // toString, hashCode, equals插件
        if (generatorConfig.isNeedToStringHashcodeEquals()) {
            PluginConfiguration pluginConfiguration1 = new PluginConfiguration();
            pluginConfiguration1.addProperty("type", "org.mybatis.generator.plugins.EqualsHashCodePlugin");
            pluginConfiguration1.setConfigurationType("org.mybatis.generator.plugins.EqualsHashCodePlugin");
            context.addPluginConfiguration(pluginConfiguration1);
            PluginConfiguration pluginConfiguration2 = new PluginConfiguration();
            pluginConfiguration2.addProperty("type", "org.mybatis.generator.plugins.ToStringPlugin");
            pluginConfiguration2.setConfigurationType("org.mybatis.generator.plugins.ToStringPlugin");
            context.addPluginConfiguration(pluginConfiguration2);
        }
        // limit/offset插件
        if (generatorConfig.isOffsetLimit()) {
            if (DbType.MySQL.name().equals(selectedDatabaseConfig.getDbType())
                    || DbType.PostgreSQL.name().equals(selectedDatabaseConfig.getDbType())) {
                PluginConfiguration pluginConfiguration = new PluginConfiguration();
                pluginConfiguration.addProperty("type", "com.zzg.mybatis.generator.plugins.MySQLLimitPlugin");
                pluginConfiguration.setConfigurationType("com.zzg.mybatis.generator.plugins.MySQLLimitPlugin");
                context.addPluginConfiguration(pluginConfiguration);
            }
        }
        context.setTargetRuntime("MyBatis3");

        List<String> warnings = new ArrayList<>();
        Set<String> fullyqualifiedTables = new HashSet<>();
        Set<String> contexts = new HashSet<>();
        ShellCallback shellCallback = new DefaultShellCallback(true); // override=true
        MyBatisGenerator myBatisGenerator = new MyBatisGenerator(configuration, shellCallback, warnings);
        myBatisGenerator.generate(progressCallback, contexts, fullyqualifiedTables);
    }

    public void setProgressCallback(ProgressCallback progressCallback) {
        this.progressCallback = progressCallback;
    }

    public void setColumnOverrides(List<ColumnOverride> columnOverrides) {
        this.columnOverrides = columnOverrides;
    }
}
